package com.google.code.lf.gfm.ui.error;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.ui.statushandlers.AbstractStatusHandler;
import org.eclipse.ui.statushandlers.StatusAdapter;
import org.eclipse.ui.statushandlers.StatusManager;

import com.google.code.lf.commons.exception.BusinessException;
import com.google.code.lf.commons.exception.ManagerException;
import com.google.code.lf.gfm.ui.Activator;
import com.google.code.lf.gfm.ui.dialog.DialogHelper;
import com.google.code.lf.gfm.ui.log.LogHelper;

/**
 * Classe permettant la gestion des exceptions au niveau client.
 *
 * @see AbstractStatusHandler
 */
public class WorkbenchErrorHandler extends AbstractStatusHandler {

	public WorkbenchErrorHandler() {
		// Pour éviter d'avoir les erreurs en double
		SafeRunnable.setIgnoreErrors(true);
	}

	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.statushandlers.AbstractStatusHandler#handle(org.eclipse.ui.statushandlers.StatusAdapter, int)
	 */
	@Override
	public void handle(final StatusAdapter statusAdapter, final int style) {
		final IStatus status = statusAdapter.getStatus();
		this.handle(status, style);
	}

	/**
	 * Suivant le style et le statut de l'erreur, cette méthode logge l'erreur et affiche l'erreur.
	 *
	 * @param status Le status Eclipse contenant l'exception
	 * @param style Le style d'affichage de l'erreur.
	 */
	public void handle(final IStatus status, final int style) {
		LogHelper.trace("Erreur: style=" + style + ", severity=" + StatusHelper.getText(status.getSeverity()) + ", code=" + status.getCode() + ", message=" + status.getMessage() + ", exception=" + status.getException());

		// Détermination du message à afficher à l'utilisateur
		final Throwable exception = status.getException();
		String message = exception.getMessage();
		String title = "Erreur";
		boolean critique = false;

		if (exception instanceof ClientException) {
			critique = ((ClientException) exception).isCritique();
		} else if (exception instanceof ManagerException) {
			title = "Erreur Interne";
			critique= true;
		} else if (exception instanceof NullPointerException) {
			title = "Erreur Interne";
			if (message == null) {
				message = "NullPointerException";
			}
			critique= true;
		} else if (!(exception instanceof BusinessException)) {
			title = "Erreur Interne";
			critique= true;
		}

		if (message == null) {
			message = "Erreur inconnue";
		}

		switch (style) {
		case StatusManager.SHOW:
			final IStatus errorStatus = createErrorStatus(status, message, exception);

			if (critique) {
				// Affiche l'erreur dans une boite de dialogue critique.
				// On utilise un nouveau statut pour avoir un meilleur affichage.
				DialogHelper.errorCriticDialog(errorStatus);
			} else {
				DialogHelper.errorDialog(message, title);
			}

			// On loggue l'erreur
			LogHelper.log(message, status, exception);

			// On loggue l'erreur dans le log d'Eclipse
			// On utilise un nouveau statut au niveau OK pour éviter d'afficher deux fois le message d'erreur.
			Platform.getLog(Activator.getDefault().getBundle()).log(errorStatus);

			break;
		default:
			// On n'affiche pas l'erreur. Mais on la loggue en tant que Warning
			LogHelper.warn(message, exception);
			break;
		}
	}

	private IStatus createErrorStatus(final IStatus status, final String message, final Throwable exception) {
		// Création d'un status avec status fils, pour afficher dans le bloc "details" de la boite de dialogue
		// On n'affichera pas la stackTrace java, mais un arbre de messages IStatus, correspondant chacun à une exception
		final MultiStatus errorStatus = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, message, exception);
		StackTraceElement[] stackElems = exception.getStackTrace();
		for (final StackTraceElement stackTraceElement : stackElems) { //chaque ligne de la trace de l'exception mère
			errorStatus.add(new Status(status.getSeverity(), Activator.PLUGIN_ID, stackTraceElement.toString().replace('\n', ' ') ));
		}

		// On ajoute un Status fils (sinon la boite de dialogue ne s'affiche pas).
		errorStatus.add(new Status(status.getSeverity(), Activator.PLUGIN_ID, ""));//ligne vide
		errorStatus.add(new Status(status.getSeverity(), Activator.PLUGIN_ID, //1ere exception fille
				(exception.getCause() != null ? "Caused by :" + exception.getCause().getClass().getName() + ":" + exception.getCause().getMessage() : "").replace('\n', ' ')));
		if (exception.getCause() != null) {
			stackElems = exception.getCause().getStackTrace();
			for (final StackTraceElement stackTraceElement : stackElems) { //chaque ligne de la trace
				errorStatus.add(new Status(status.getSeverity(), Activator.PLUGIN_ID, " " + stackTraceElement.toString().replace('\n', ' ') ));
			}
		}

		return errorStatus;
	}

}
